package com.maikang.exammall.service;

import com.maikang.exammall.entity.Bill;
import com.maikang.exammall.entity.Order;
import com.maikang.exammall.entity.OrderItem;
import com.maikang.exammall.entity.constant.BillStatus;
import com.maikang.exammall.entity.constant.OrderStatus;
import com.maikang.exammall.entity.constant.TransType;
import com.maikang.exammall.repository.BillRepository;
import com.maikang.exammall.repository.CommissionRepository;
import com.maikang.exammall.repository.HospitalRepository;
import com.maikang.exammall.repository.OrderRepository;
import com.maikang.exammall.task.async.PackageAsyncTask;
import com.maikang.exammall.task.async.WechatMessageTemplate;
import com.maikang.exammall.util.DateUtil;
import com.maikang.exammall.util.SmsUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.text.SimpleDateFormat;
import java.util.*;


@Log4j2
@Service
@RequiredArgsConstructor
public class BillService extends BaseService<Bill, Long, BillRepository> {
    private final OrderRepository orderRepository;
    private final WechatMessageTemplate wechatMessageTemplate;
    private final CommissionRepository commissionRepository;
    private final PackageAsyncTask packageAsyncTask;
    private final SmsUtil smsUtil;
    private final HospitalRepository hospitalRepository;

    @Transactional(rollbackFor = Exception.class)
    public boolean wechatPayCallback(Map<String, String> kvm) {
        String outTradeNo = kvm.get("out_trade_no");
        Long totalFee = Long.valueOf(kvm.get("total_fee"));
        Date timeEnd = DateUtil.parseTxTime(kvm.get("time_end"));
        try {
            Bill bill = repository.findByOutTradeNoAndTransType(outTradeNo, TransType.INCOMES);
            if (bill == null) return false;

            // 已经处理过， 直接返回SUCCESS
            if (bill.getStatus().equals(BillStatus.PAID)) {
                log.info("wechat notify: outTradeNo={} has already been processed, so ignoring..", outTradeNo);
                return true;
            }
            // 回调的订单总价与数据库价格不符
            if (!bill.getTotalFee().equals(totalFee)) {
                log.warn("wechat notify: outTradeNo={} total fee of notify result {} is not equal to what is in db {}", outTradeNo, totalFee, bill.getTotalFee());
                return false;
            }
            // update bill to database
            bill.setStatus(BillStatus.PAID);
            bill.setTransactionId(kvm.get("transaction_id"));
            bill.setTimeEnd(timeEnd);
            repository.save(bill);

            // update Order status and OrderItems' status and Commission status
            Order order = orderRepository.findByOutTradeNo(outTradeNo);
            List<OrderItem> orderItems = order.getOrderItems();
            List<Long> itemIds = new ArrayList<>();
            Map<Long, Integer> pkgIdCountMap = new HashMap<>();

            order.setStatus(OrderStatus.PAID);
            orderItems.forEach(orderItem -> {
                orderItem.setStatus(OrderStatus.PAID);
                orderItem.setOrder(order);
                itemIds.add(orderItem.getId());
                Integer count = pkgIdCountMap.getOrDefault(orderItem.getPkgId(), 0) + 1;
                pkgIdCountMap.put(orderItem.getPkgId(), count);
            });
            order.setOrderItems(orderItems);
            Order save = orderRepository.save(order);

            commissionRepository.updateStatusByOrderItemIds(itemIds, OrderStatus.PAID);
            packageAsyncTask.incrementPackageSoldCount(pkgIdCountMap);

            // 微信推送通知运营处理付款订单
            wechatMessageTemplate.NotifyOperatorsAfterOrderIsPaid(save);

            // 微信推送给客户下单成功
            wechatMessageTemplate.NotifyClientAfterOrderIsPaid(save);

            // 短信推送给体检人下单成功
            String address = hospitalRepository.findAddressById(orderItems.get(0).getHospitalId());
            SimpleDateFormat f = new SimpleDateFormat("yyyy年MM月dd日");
            for (OrderItem item : orderItems) {
                String[] paramSet = new String[5];
                paramSet[0] = item.getExamineeName();
                paramSet[1] = outTradeNo;
                paramSet[2] = f.format(item.getExamineDate());
                paramSet[3] = order.getOrderItems().get(0).getHospitalName();
                paramSet[4] = address;
                smsUtil.sendAppointmentOkSuccessSMS(paramSet, "+86" + item.getExamineeMobile());
            }
            return true;
        } catch (Exception e) {
            log.error("wechatPayCallback service failed ", e);
            return false;
        }
    }
}
